from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Union, Any, Dict, Set
from jose import jwt as JoseJwt
from src.backend.utils.envLoader import appConfig
from fastapi.concurrency import run_in_threadpool as runInThreadpool

passwordContext = CryptContext(schemes=["bcrypt"], deprecated="auto")

ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7

# 令牌黑名单，用于存储已注销的令牌
tokenBlacklist: Set[str] = set()
# 黑名单过期时间映射，用于定期清理黑名单
blacklistExpiry: Dict[str, datetime] = {}


async def getHashedPassword(password: str) -> str:
    return await runInThreadpool(passwordContext.hash, password)


async def verifyPassword(password: str, hashedPass: str) -> bool:
    return await runInThreadpool(passwordContext.verify, password, hashedPass)


def createAccessToken(subject: Union[str, Any], expiresDelta: timedelta = None) -> str:
    expire = datetime.utcnow() + (expiresDelta if expiresDelta else timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    toEncode = {"exp": expire, "sub": str(subject)}
    encodedJwt = JoseJwt.encode(toEncode, appConfig.JWT_SECRET_KEY, appConfig.JWT_ALGORITHM)
    return encodedJwt


def createRefreshToken(subject: Union[str, Any], expiresDelta: timedelta = None) -> str:
    expire = datetime.utcnow() + (expiresDelta if expiresDelta else timedelta(minutes=REFRESH_TOKEN_EXPIRE_MINUTES))
    toEncode = {"exp": expire, "sub": str(subject)}
    encodedJwt = JoseJwt.encode(toEncode, appConfig.JWT_REFRESH_SECRET_KEY, appConfig.JWT_ALGORITHM)
    return encodedJwt


def addToBlacklist(token: str) -> None:
    """
    将令牌添加到黑名单
    """
    try:
        # 解析令牌以获取过期时间
        payload = JoseJwt.decode(token, appConfig.JWT_SECRET_KEY, algorithms=[appConfig.JWT_ALGORITHM])
        exp = datetime.fromtimestamp(payload["exp"])
        
        # 添加到黑名单
        tokenBlacklist.add(token)
        blacklistExpiry[token] = exp
        
        # 清理过期的黑名单条目
        cleanupBlacklist()
    except Exception:
        # 如果解析失败，可能是刷新令牌或无效令牌，忽略
        pass


def isBlacklisted(token: str) -> bool:
    """
    检查令牌是否在黑名单中
    """
    return token in tokenBlacklist


def cleanupBlacklist() -> None:
    """
    清理已过期的黑名单条目
    """
    now = datetime.utcnow()
    expiredTokens = [token for token, expiry in blacklistExpiry.items() if expiry < now]
    
    for token in expiredTokens:
        if token in tokenBlacklist:
            tokenBlacklist.remove(token)
        if token in blacklistExpiry:
            del blacklistExpiry[token] 